﻿//
// Created by Administrator on 2022/6/15.
//

//
// Created by gogoqiu on 2022/1/11.
//

#include "repDialog.h"
#include "wx/wx.h"
#include "wx/confbase.h"
#include "wx/stdpaths.h"
#include "wx/sysopt.h"
#include <wx/fs_zip.h> // ZIP filesystem support
#include <wx/tipdlg.h>
#include <wx/cshelp.h>
#include <wx/image.h>
#include <wx/dir.h>
#include <filesystem>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fcntl.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utime.h>
#include <share.h>
#include "action_event.h"
#include "ids.h"
#include "../../repLib/src/func.h"
#include "MyApp.h"
#include "utils.h"

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/locale.hpp>

using namespace std;
#define TIMER_ID 1000
#define  BLOCK_SIZE 4096

//mysql
#define key_mysql_server "mysql-server"
#define key_mysql_port "mysql-port"
#define key_mysql_user "mysql-user"
#define key_mysql_password "mysql-password"
#define key_mysql_database "mysql-database"
//ssh
#define key_ssh_server "ssh-server"
#define key_ssh_port "ssh-port"
#define key_ssh_user "ssh-user"
#define key_ssh_password "ssh-password"
#define key_ssh_path "ssh-path"

#define key_thunder    "thunder"
#define key_mule      "mule"
#define key_remove_after_copy "remove-after-copy"
namespace p{
    string mysql_server;
    int    mysql_port;
    string mysql_user;
    string mysql_password;
    string mysql_database;

    string ssh_server;
    int    ssh_port;
    string ssh_user;
    string ssh_password;
    string ssh_path;

    string thunder;
    string mule;
    bool move_after_copy;
}

BEGIN_EVENT_TABLE ( repDialog, wxFrame )
    EVT_MENU ( ACTION_EVENT, repDialog::OnActionEvent )
END_EVENT_TABLE()

repDialog* repDialog::dialog = nullptr;

void repDialog::parseArguments(){

    boost::property_tree::ptree root_node, tag_system;
    boost::property_tree::ini_parser::read_ini( wxGetApp().argv[1].ToStdString(),
                                                root_node);
    tag_system = root_node.get_child("repository");

    if(tag_system.count( key_ssh_server ) != 1) {
        GuiCmdParamErrorMsg( key_ssh_server );
        this->Close();
    }
    p::ssh_server = tag_system.get<string>( key_ssh_server );

    if(tag_system.count( key_ssh_port ) != 1) {
        GuiCmdParamErrorMsg( key_ssh_port );
        this->Close();
    }
    p::ssh_port = tag_system.get<int>( key_ssh_port );

    if(tag_system.count( key_ssh_user ) != 1) {
        GuiCmdParamErrorMsg( key_ssh_user );
        this->Close();
    }
    p::ssh_user = tag_system.get<string>( key_ssh_user );

    if(tag_system.count( key_ssh_password ) != 1) {
        GuiCmdParamErrorMsg( key_ssh_password );
        this->Close();
    }
    p::ssh_password = tag_system.get<string>( key_ssh_password );

    if(tag_system.count( key_ssh_path ) != 1) {
        GuiCmdParamErrorMsg( key_ssh_path );
        this->Close();
    }
    p::ssh_path = tag_system.get<string>( key_ssh_path );
    //mysql
    if(tag_system.count( key_mysql_server ) != 1) {
        GuiCmdParamErrorMsg( key_mysql_server );
        this->Close();
    }
    p::mysql_server = tag_system.get<string>( key_mysql_server );

    if(tag_system.count( key_mysql_port ) != 1) {
        GuiCmdParamErrorMsg( key_mysql_port );
        this->Close();
    }
    p::mysql_port = tag_system.get<int>( key_mysql_port );

    if(tag_system.count( key_mysql_user ) != 1) {
        GuiCmdParamErrorMsg( key_mysql_user );
        this->Close();
    }
    p::mysql_user = tag_system.get<string>( key_mysql_user );

    if(tag_system.count( key_mysql_password ) != 1) {
        GuiCmdParamErrorMsg( key_mysql_password );
        this->Close();
        this->Close();
    }
    p::mysql_password = tag_system.get<string>( key_mysql_password );

    if(tag_system.count( key_mysql_database ) != 1) {
        GuiCmdParamErrorMsg( key_mysql_database );
        this->Close();
    }
    p::mysql_database = tag_system.get<string>( key_mysql_database );

    if(tag_system.count( key_thunder ) != 1) {
        GuiCmdParamErrorMsg( key_thunder );
        this->Close();
    }
    p::thunder = tag_system.get<string>( key_thunder );

    if(tag_system.count( key_mule ) != 1) {
        GuiCmdParamErrorMsg( key_mule );
        this->Close();
    }
    p::mule = tag_system.get<string>( key_mule );
    if(tag_system.count( key_remove_after_copy ) != 1) {
        GuiCmdParamErrorMsg( key_remove_after_copy );
        this->Close();
    }
    p::move_after_copy = tag_system.get<bool>( key_remove_after_copy );
}

void repDialog::buildMenu()
{
    auto *menuFile = new wxMenu;
    //
    menuFile->Append ( ID_POST_ALL, "全部发送",
                       "发送所有文件" );
    Bind( wxEVT_MENU, [this]( wxCommandEvent& event  ){
        if( wxMessageBox(wxT("确定要一起上传吗"), wxT("请确认"),
                     wxICON_QUESTION | wxYES_NO ) == wxYES ){
            for( auto p: this->panels ){
                this->postFileWorker->srcs.Add( p->dir+"/"+p->item );
                p->b->SetLabel( "已入序列" );
            }
        }
    }, ID_POST_ALL );
    menuFile->AppendSeparator();
    menuFile->Append ( wxID_EXIT );
    Bind( wxEVT_MENU, [this]( wxCommandEvent& event  ) {
        this->Destroy();
    }, wxID_EXIT );
    auto *menuHelp = new wxMenu;
    menuHelp->Append ( wxID_ABOUT );

    auto *menuBar = new wxMenuBar;
    menuBar->Append ( menuFile, "&File" );
    menuBar->Append ( menuHelp, "&Help" );
    SetMenuBar ( menuBar );
}

/*
 * 建立右击菜单，传送文件
 * */
repDialog::repDialog ( wxWindow *parent )
        : wxFrame ( parent, ID_FRAME_REPOSITORY,
                "Transfer", wxDefaultPosition, wxDefaultSize,
                    wxSYSTEM_MENU |  wxRESIZE_BORDER |
                          wxCAPTION |  wxCLIP_CHILDREN )
{
    parseArguments();
    buildMenu();
    CreateStatusBar();
    SetLabel("Transfer");
    //SetStatusText ( "Welcome to wxWidgets!" );

    SetClientSize ( wxSize ( 800, 800 ) );

    dialogRootBoxSizer = new wxBoxSizer ( wxVERTICAL );

    auto clients_static_box_sizer = new wxStaticBoxSizer( new wxStaticBox( this,
            wxID_ANY, wxT("CLIENTS") ), wxVERTICAL );
    m_listbox = new wxScrolledWindow(clients_static_box_sizer->GetStaticBox(),
                                     wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
    m_listbox->SetScrollRate(5, 5 );
    scroll_window_sizer = new wxBoxSizer( wxVERTICAL );
    m_listbox->SetSizer( scroll_window_sizer );
    clients_static_box_sizer->Add( m_listbox, 1, wxEXPAND | wxALL, 0 );

    dialogRootBoxSizer->Add( clients_static_box_sizer, 1, wxEXPAND|wxALL, 0 );

    wxSize size ( 300, 500 );

    m_logBox = new wxTextCtrl ( this, wxID_ANY, wxEmptyString,
            wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE );
    dialogRootBoxSizer->Add ( m_logBox,  1,  wxEXPAND|wxALL, 5  );

    this->SetSizer ( dialogRootBoxSizer );
    buildList();
    postFileWorker = new PostFileWorker( this );
    postFileWorker->Run();
}

void repDialog::buildList(){
    wxString dir_path;
    wxString szDirName;
    wxString fullPath;
    dir_path = wxString::FromUTF8( p::thunder );
    if( !dir_path.empty() && wxDir::Exists( dir_path ) ) {
        wxDir dir( dir_path );
        if (dir.IsOpened()) {
            wxString filename;
            int flags = wxDIR_DIRS | wxDIR_FILES ;
            bool bExist = dir.GetFirst(&filename, wxEmptyString, flags);
            for (; bExist; bExist = dir.GetNext(&filename)){
                // 获取文件的绝对路径
                wxFileName file( dir_path + "/" + filename );
                file.MakeAbsolute(szDirName);
                fullPath = file.GetFullPath();
                // 判断是目录还是文件
                if (wxFileName::DirExists(fullPath)) {
                    //build list
                    auto *item = new rscItemPanel( this,
                                                   m_listbox, dir_path, filename,
                                                   rscItemPanel::ItemType::thunder );
                    panels.push_back( item );
                    scroll_window_sizer->Add( item,
                                              0, wxALL | wxEXPAND, 5 );
                }
            }
            dir.Close();
        }
    }
    dir_path = wxString::FromUTF8( p::mule );
    if( !dir_path.empty() && wxDir::Exists( dir_path ) ) {
        wxDir dir( dir_path );
        if (dir.IsOpened()) {
            wxString filename;
            int flags = wxDIR_DIRS | wxDIR_FILES ;
            bool bExist = dir.GetFirst(&filename, wxEmptyString, flags);
            for (; bExist; bExist = dir.GetNext(&filename)){
                // 获取文件的绝对路径
                wxFileName file( dir_path + "/" + filename );
                file.MakeAbsolute(szDirName);
                fullPath = file.GetFullPath();
                // 判断是目录还是文件
                if (wxFileName::FileExists(fullPath)) {
                    //build list
                    auto *item = new rscItemPanel( this,
                                                   m_listbox, dir_path, filename,
                                                   rscItemPanel::ItemType::mule );
                    panels.push_back( item );
                    scroll_window_sizer->Add( item,
                                              0, wxALL | wxEXPAND, 5 );
                }
            }
            dir.Close();
        }
    }
}

repDialog::~repDialog()
{
    for ( int i = 0; i < panels.size(); i++ ) {
        //panels.at(i)->Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( PreferencesFrame::OnClickItem ), NULL, this );
    }
    if( postFileWorker )
        postFileWorker->Kill();
    dialog  = nullptr;
}

void repDialog::OnExit ( wxCommandEvent& event )
{
    Close ( true );
}

void repDialog::OnActionEvent ( wxCommandEvent &event )
{
    const int token = event.GetInt();
    const long liType = event.GetExtraLong();
    switch ( token ) {
        case TOKEN_INFO:
            break;
        case TOKEN_ERROR:
            break;
        case TOKEN_ACTION_START:
            break;
        case TOKEN_LOG:{
            wxString log = event.GetString();
            addLog( log );
            break;
        }
        case TOKEN_SCP_PERCENT:{
            auto *value = (int*)event.GetClientData();
            printf( "%d\n", *value );
            //m_gauge1->SetValue(*value);
            delete value;
            break;
        }
    }
}

void repDialog::addLog( const wxString& log ){
    wxString tmp = log;
    if( !log.EndsWith("\n"))
        tmp = tmp +  _T("\n") ;
    if( m_logBox )
        this->m_logBox->AppendText( tmp );
}

void repDialog::update_percent( const wxString &dir, int percent ) {
    for( auto p : panels ){
        if( p->item == dir ){
            p->g->SetValue( percent );
            break;
        }
    }
}

int repDialog::PostFileWorker::RunSql( const char* _sql, char*  msg  ){
    int res;
    res = mysql_query( conn_ptr, _sql );
    if (!res) {
        return mysql_affected_rows( conn_ptr );
    } else {
        sprintf(msg, "mysql error %d: %s, sql: %s\n",
                mysql_errno( conn_ptr ),
                mysql_error( conn_ptr ), _sql );
        return -1;
    }
}

//watching
//电驴版
//torrent版
//额外信息
int repDialog::PostFileWorker::RunSql( const wxString &sql, char*  msg  ){

    const char *_sql;
    _sql = sql.mb_str(wxConvUTF8);

    return RunSql( _sql, msg );
}

int repDialog::PostFileWorker::insertTorrent( const wxString& dir, const wxString &torrent_path ) {
    wxFile f( torrent_path );
    if( !f.IsOpened() )
        return -1;
    char *buff = new char[f.Length()];
    ssize_t read = 0, sum = 0;
    while( !f.Eof() ){
        read = f.Read( &buff[sum], 4096 );
        if( read ==-1 ){
            return -1;
        }
        sum += read;
        f.Seek( sum );
    }
    char* encoded = new char[ f.Length() * 2  ];
    unsigned long len = mysql_real_escape_string( conn_ptr, encoded, buff, sum );
    char* sql = new char[ len + 256 ];
    const char* _dir = dir.mb_str(wxConvUTF8);
    sprintf( sql, "insert into torrent( dir, torrent ) values( '%s', '%s')" ,
            _dir, encoded );
    char errMsg[256];
    int ret = RunSql( sql, errMsg );
    if( ret< 0 )
        addLog( errMsg );
    delete[]  sql;
    delete[]  encoded;
    delete [] buff;
    return ret;
}

repDialog::PostFileWorker::PostFileWorker(repDialog *panel) {
    this->dialog = panel;
    lost_connection = false;
    //连接mysql
    conn_ptr = mysql_init(nullptr);
    if (!conn_ptr) {
        addLog("mysql初始化失败");
    }
    //?
    conn_ptr = mysql_real_connect( conn_ptr, p::mysql_server.c_str(),
                                   p::mysql_user.c_str(), p::mysql_password.c_str(),
                                   p::mysql_database.c_str(), p::mysql_port, nullptr, 0);
    if (!conn_ptr) {
        addLog("mysql连接失败");
    }else
        addLog("mysql已连接");
    mysql_set_character_set( conn_ptr, "utf8");
    /////连接ssh//////////
    int rc = libssh2_init ( 0 );
    if ( rc ) {
        addLog( "libssh2 initialization failed " );
    }
#ifdef WIN32
    WSADATA wsa_data;
    int err;

    err = WSAStartup ( MAKEWORD ( 2, 0 ), &wsa_data );
    if ( err != 0 ) {
        addLog("WSAStartup failed with error" );
        return;
    }
#endif
    sock = socket ( AF_INET, SOCK_STREAM, 0 );
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons ( p::ssh_port );
    hostent *host = gethostbyname ( p::ssh_server.c_str() );
    memmove ( & ( sin.sin_addr ), host->h_addr, host->h_length );

    if ( connect ( sock, ( struct sockaddr* ) ( &sin ),
                   sizeof ( struct sockaddr_in ) ) != 0 ) {
        addLog( _T("failed to connect!" ) );
        return;
    }
    session = libssh2_session_init();
    if ( !session ) {
        addLog( _T("libssh2初始化失败！" ) );
        return;
    }
    rc = libssh2_session_handshake ( session, sock );

    if ( rc ) {
        addLog( _T("Failure establishing SSH session: %d\n") );
        return;
    }
    const char* fingerprint = libssh2_hostkey_hash ( session, LIBSSH2_HOSTKEY_HASH_SHA1 );
    if ( libssh2_userauth_password ( session, p::ssh_user.c_str()
            , p::ssh_password.c_str() ) ){
        addLog ( _T( "Authentication by password failed." ));
        return;
    }
    addLog( "ssh已连接");
}

repDialog::PostFileWorker::~PostFileWorker(){
    if( conn_ptr )
        mysql_close(conn_ptr);
    if( sock!=-1 )
        closesocket( sock );
    if ( session ) {
        libssh2_session_disconnect ( session, "Normal Shutdown, Thank you for playing" );
        libssh2_session_free ( session );
    }
    libssh2_exit();
}

static void FetchDirsFiles( const wxString& szDirName, wxArrayString& dirs, wxArrayString& files ) {
    wxDir dir(szDirName);
    if (dir.IsOpened()) {
        wxString filename;
        int flags = wxDIR_FILES | wxDIR_DIRS | wxDIR_HIDDEN;
        bool bExist = dir.GetFirst(&filename, wxEmptyString, flags);
        for (; bExist; bExist = dir.GetNext(&filename))
        {
            // 获取文件的绝对路径
            wxFileName file( szDirName + "/" + filename );
            file.MakeAbsolute(szDirName);
            wxString fullPath = file.GetFullPath();

            // 判断是目录还是文件
            if (wxFileName::DirExists(fullPath)) {
                dirs.Add(fullPath);
                FetchDirsFiles(fullPath, dirs, files );
            } else {
                files.Add(fullPath);
            }
        }
        dir.Close();
    }
}

void *repDialog::PostFileWorker::Entry() {
    bool have_srcs = false;
    //没有传输过文件多少个一秒周期了
    int idle_count = 0;
    bool ok ;
    char cmd[256], response[256];
    const char* s;
    wxString rel_path;
    wxString remote_dir;
    wxString remote_path;
    while( true ){
        ok = true;
        if( !srcs.empty() ){
            if ( wxFileName::FileExists( srcs[0]) ){
                //计算出来相对路径上传, 计算出
            }else if ( wxFileName::DirExists( srcs[0]) ){
                idle_count = 0;
                wxArrayString dirs;
                wxArrayString files;
                //减少迅雷根目录
                wxString dir = srcs[0].substr( wxString::FromUTF8( p::thunder ).size() + 1 );
                sprintf( cmd, "mkdir 2>&1 \"%s/%s\"", p::ssh_path.c_str(),
                        (const char*)dir.mb_str( wxConvUTF8 ) );
                memset( response, 0, 256 );
                if( ssh_cmd ( cmd, response ) <0 ){
                    ok = false;
                }
                if( strlen(response)>0 ){
                    addLog( "目录已存在: " + dir + "," + wxString::FromUTF8( response ) ) ;
                    ok = false;
                }
                FetchDirsFiles( srcs[0], dirs, files );
                //计算出来相对路径上传
                unsigned int idx = 0;
                if( ok ){
                    for( const auto& f : files ){
                        ok = false;
                        if( f.substr( f.find_last_of( "." ) ).Lower()==".torrent"){
                            if( insertTorrent( dir, f ) < 0 )
                                break;
                        }else{
                            //相对目录
                            rel_path = f.substr( wxString::FromUTF8( p::thunder ).size() + 1 );
                            rel_path.Replace( "\\", "/" );
                            remote_path = wxString::FromUTF8( p::ssh_path ) + "/" + rel_path;

                            remote_dir = remote_path.substr( 0, remote_path.find_last_of("/") );
                            s = remote_dir.mb_str(wxConvUTF8);

                            memset( cmd, 0, 256 );
                            memset( response, 0, 256 );
                            sprintf( cmd, "mkdir -p \"%s\"", s );
                            addLog( wxString::FromUTF8( cmd ) );
                            if( ssh_cmd ( cmd, response ) <0 ){
                                break;
                            }

                            if( scp( f, remote_path )!=0 ){
                                break;
                            }
                        }
                        ok = true;
                        idx ++;
                        dialog->update_percent( dir, idx * 100/files.size() );
                    }
                }

                if( ok ) {
                    char errMsg[256];
                    const char *p = dir.mb_str(wxConvUTF8);
                    int len = strlen(p);
                    char encoded[1024];
                    mysql_real_escape_string(conn_ptr, encoded, p, len);
                    char sql[1024];
                    sprintf(sql, "insert into rep( `name`, upload_time ) values( '%s', NOW())", encoded);
                    if (RunSql(sql, errMsg) < 0) {
                        addLog(errMsg);
                        ok = false;
                    }
                    dialog->update_percent( dir, 100 );
                }
                if( ok ) {
                    if( p::move_after_copy ){
                        if( wxFileName( srcs[0]+"\\" ).Rmdir( wxPATH_RMDIR_RECURSIVE ) ){
                            addLog( "源目录已删除: " + srcs[0] );
                        }else {
                            addLog("源目录删除失败: " + srcs[0] );
                            ok = false;
                        }
                    }
                }
                if( ok )
                    addLog( "\"" + dir +"\"已完成" );
            }
            //无论成功不成功都不用反复做了
            srcs.erase( srcs.begin() );
        }else{
            if( idle_count>60*2 ) {
                pingMySql();
                pingSsh();
                idle_count = 0 ;
            }
            idle_count++;
        }
        Sleep( 1000 );
    }
    return nullptr;
}

int repDialog::PostFileWorker::scp_process_block( LIBSSH2_CHANNEL *channel, const char *buffer, size_t nRead ){
    int rc;
    do {
        /* write the same data over and over, until error or completion */
        rc = libssh2_channel_write ( channel, buffer, nRead );
        if ( rc < 0 ) {
            addLog( "通道写失败" );
            //break;
            return -1;
        } else {
            /* rc indicates how many bytes were written this time */
            buffer += rc;
            nRead -= rc;
        }
    } while ( nRead );
    return 0;
}

int repDialog::PostFileWorker::process_partial_block( LIBSSH2_CHANNEL* channel, char* buffer, size_t sum ){
    if ( sum > 0 ) {
        if( scp_process_block( channel, buffer, sum ) != 0 )
            return -1;
    }
    libssh2_channel_send_eof ( channel );

    fprintf ( stderr, "Waiting for EOF\n" );
    libssh2_channel_wait_eof ( channel );

    fprintf ( stderr, "Waiting for channel to close\n" );
    libssh2_channel_wait_closed ( channel );
    libssh2_channel_free ( channel );
    channel = nullptr;
    return 0;
}

int repDialog::PostFileWorker::ssh_cmd( const wxString& cmd , char* response ){
    const char* s = cmd.mb_str(wxConvUTF8);
    return ssh_cmd( s, response );
}

int repDialog::PostFileWorker::ssh_cmd( const char* cmd, char* response ){
    LIBSSH2_CHANNEL* channel;
    channel = libssh2_channel_open_session( session );
    if ( !channel ) {
        char *errmsg;
        int errLen, err = libssh2_session_last_error ( session, &errmsg, &errLen, 0 );
        wxString tmp;
        tmp.sprintf( "Unable to open a session: (%d) %s", err, errmsg );
        addLog( "建立ssh通道失败, 传输中止: " + tmp );
        return -1;
    }

    int rc = libssh2_channel_exec( channel, cmd );
    if( rc!=0 ){
        addLog( "执行命令错误:" + wxString( cmd ) );
        return -1;
    }
    int read = 0, sum = 0;
    while(true) {
        read = libssh2_channel_read( channel, &response[sum], BLOCK_SIZE );
        if( read > 0 ) {
            sum+=read;
        } else {
            break;
        }
    }
    libssh2_channel_free(channel);
    channel = nullptr;
}

int repDialog::PostFileWorker::scp(  const char* src,  libssh2_int64_t size,
                                     time_t modified, const char* dest ) {
    struct _stat64 stat64;
    int hFile;
    _sopen_s(&hFile, src, _O_RDWR, _SH_DENYNO, 0);
    if (_fstat64( hFile, &stat64 ) == -1){

    }
    _close (hFile);
    return 0;
}

int repDialog::PostFileWorker::scp( const wxString &src, const wxString &dest ) {

    addLog( "上传文件：" + dest.substr( dest.find_last_of( "/" )+1));
    LIBSSH2_CHANNEL* channel;

    char buffer[BLOCK_SIZE + 72];
    wxFileName localFile( src );
    channel = libssh2_scp_send64( session, dest.mb_str(wxConvUTF8),
                                  511, localFile.GetSize().GetValue(),
                                  localFile.GetModificationTime().GetTicks(), 0 );
    if ( !channel ) {
        char *errmsg;
        int errLen, err = libssh2_session_last_error ( session, &errmsg, &errLen, 0 );
        wxString tmp;
        tmp.sprintf( "Unable to open a session: (%d) %s", err, errmsg );
        addLog( "建立ssh通道失败, 传输中止: " + tmp );
        return -1;
    }
    wxFile file( localFile.GetFullPath() );
    if ( !file.IsOpened() ){
        addLog( "文件打开失败: "+ localFile.GetFullPath() );
        return -1;
    }
    size_t n;
    size_t sum = 0;
    bool copy_done = false;
    while ( true ) {
        sum = 0;
        while (true) {
            n = file.Read(buffer + sum, BLOCK_SIZE - sum);
            sum += n;
            if (sum == BLOCK_SIZE)
                break;
            if (n == 0) {
                if (file.Error()) {
                    addLog("文件异常:" + src);
                    return -1;
                }
                process_partial_block(channel, buffer, sum );
                copy_done = true;break;
            }
            if (file.Eof()){
                process_partial_block(channel, buffer, sum );
                copy_done = true;break;
            }
        }
        if( copy_done ) break;
        if( scp_process_block( channel, buffer, BLOCK_SIZE )== -1 ){
            addLog( "传输异常:" + src );
            break;
        }
    }
    return 0;
}

void repDialog::PostFileWorker::pingMySql() {
    int retVal = mysql_query(conn_ptr, "SET NAMES UTF8");
    wxDateTime dateTime = wxDateTime::Now();
    wxString tmp;
    wxString t = dateTime.FormatTime();
    if (retVal != 0) {
        tmp.sprintf( wxT("%s: %s"), t, mysql_error(conn_ptr) );
    }else{
        tmp.sprintf( wxT("%s: %s"), t, wxT("延续MySql连接") );
    }
    addLog( tmp );
}

void repDialog::PostFileWorker::pingSsh() {
    LIBSSH2_CHANNEL* channel;
    channel = libssh2_channel_open_session( session );
    if ( !channel ) {
        char *errmsg;
        int errLen, err = libssh2_session_last_error ( session, &errmsg, &errLen, 0 );
        wxString tmp;
        tmp.sprintf( "Unable to open a session: (%d) %s", err, errmsg );
        addLog( "建立ssh通道失败, 无法执行命令: " + tmp );
        return;
    }
    int rc = libssh2_channel_exec( channel, "ls /" );
    if( rc!=0 ){
        addLog( "ping ssh 失败" );
        return;
    }
    libssh2_channel_send_eof ( channel );

    fprintf ( stderr, "Waiting for EOF\n" );
    libssh2_channel_wait_eof ( channel );

    fprintf ( stderr, "Waiting for channel to close\n" );
    libssh2_channel_wait_closed ( channel );
    libssh2_channel_free ( channel );
    channel = nullptr;
}

void repDialog::PostFileWorker::addLog( const wxString &log) {
    dialog->addLog( log );
}

repDialog::rscItemPanel::rscItemPanel( repDialog *d , wxWindow *parent, const wxString& dir,
                            const wxString& item, ItemType type ):
        wxPanel( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL  ){
    this->dialog = d;
    this->type = type;
    this->dir = dir;
    this->item = item;
    this->SetSizeHints( wxDefaultSize, wxDefaultSize );
    wxString choices[] = { "video", "movie", "music", "game" };
    wxBoxSizer* sizer;
    sizer = new wxBoxSizer( wxHORIZONTAL );

    auto* static_dir = new wxStaticText( this, wxID_ANY,
                                         ( item ), wxDefaultPosition, wxDefaultSize, 0 );
    static_dir->Wrap( -1 );
    static_dir->SetMinSize( wxSize( 100,-1 ) );

    sizer->Add( static_dir, 3, wxALL, 5 );

    t = new wxComboBox( this, wxID_ANY,
                        choices[0], wxDefaultPosition, wxDefaultSize, 3, choices );
    sizer->Add( t, 1, wxALL, 0 );

    b = new wxButton( this, ID_ITEM_POST,
                  "发送", wxDefaultPosition, wxDefaultSize  );
    b->Bind( wxEVT_BUTTON, [ this ]( wxCommandEvent event ){
        if( this->b->GetLabel() == "发送") {
            wxString tmp = this->dir;
            tmp = tmp + "\\" + this->item;
            auto src = this->dialog->postFileWorker->srcs;
            this->dialog->postFileWorker->srcs.Add( tmp );
            this->b->SetLabel( "已入序列" );
        }
    });
    sizer->Add( b );

    g = new wxGauge( this, wxID_ANY, 101,
                     wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL );
    g->SetValue( 0 );
    sizer->Add( g, 3, wxALL, 0 );
    this->SetSizer( sizer );
    this->Layout();
    sizer->Fit( this );
    if( type==mule)
        this->SetBackgroundColour( wxColour( 0, 255, 0 ));
    else if( type==thunder )
        this->SetBackgroundColour( wxColour( 0, 255, 255 ));
    this->Bind( wxEVT_LEFT_DCLICK, [ this ]( wxMouseEvent e ){
        if( b->GetLabel()=="发送")
            this->Destroy();
    });
}
